/*
 * Copyright (c) 2008-2018, RF-Embedded GmbH
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "TestWidget.h"
#include "ui_TestWidget.h"

#include <QTcpSocket>
#include <QHostAddress>
#include <QSerialPort>
#include <QInputDialog>
#include <QTimer>

#include "impl/ConsoleTrace.h"
#include "impl/Sleeper.h"

#include <QrfeReaderInterface.h>
#include <protocol/QrfeProtocolHandler.h>
#include <protocol/QrfePURprotocolHandler.h>
#include <protocol/QrfeTagEvent.h>
#include <protocol/QrfeProtocolConstants.h>

TestWidget::TestWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TestWidget)
{
    ui->setupUi(this);

    m_ph = 0;
    m_trc = 0;

    m_currentTestState = IDLE;
    m_lastTagEventValid = true;

    ui->testGroupBox->setEnabled(false);

    connect(ui->startPushButton,    SIGNAL(clicked()),  this,   SLOT(startStopExit()));
}

TestWidget::~TestWidget()
{
    delete ui;
    if(m_ph != 0)delete m_ph;
    if(m_trc != 0)delete m_trc;
}


void TestWidget::init(QString serialPort)
{
    // Open serial port
    QSerialPort* port = new QSerialPort();
    port->setPortName(serialPort);
    port->setFlowControl(QSerialPort::NoFlowControl);
    port->setParity(QSerialPort::NoParity);
    port->setDataBits(QSerialPort::Data8);
    port->setStopBits(QSerialPort::OneStop);
    port->setBaudRate(QSerialPort::Baud115200);
    if(!port->open(QIODevice::ReadWrite))
    {
        print("Could not open the serial port " + serialPort);
        delete port;
        return;
    }

    init(port);
}

void TestWidget::init(QString ip, ushort port)
{
    // Open socket
    QTcpSocket* sock = new QTcpSocket();
    sock->connectToHost(QHostAddress(ip), port);
    if(!sock->waitForConnected())
    {
        print("Could not connect to " + ip + ":" + QString::number(port));
        delete sock;
        return;
    }

    init(sock);
}

void TestWidget::init(QIODevice *dev)
{
    bool ok = false;

    // Create Tracer
    m_trc = new ConsoleTrace();
    QrfeReaderInterface::Global::m_tracer = m_trc;
    QrfeReaderInterface::Global::m_tracer->setTraceLevel(1);

    // Create Protocol Handler
    m_ph = new QrfeReaderInterface::QrfeProtocolHandler(dev);

    m_ph->setHeartBeat(QrfeReaderInterface::HEARTBEAT_OFF);

    // Connect signals
    connect(m_ph, SIGNAL(heartBeat()),
            this,   SLOT(heartBeat()));
    connect(m_ph, SIGNAL(cyclicInventory(QrfeReaderInterface::QrfeTagEvent)),
            this,   SLOT(cyclicInventory(QrfeReaderInterface::QrfeTagEvent)));
    connect(m_ph, SIGNAL(stateChanged(int)),
            this,   SLOT(stateChanged(int)));
    connect(m_ph, SIGNAL(statusRegisterChanged(qulonglong)),
            this,   SLOT(statusRegisterChanged(qulonglong)));
    connect(m_ph, SIGNAL(gpioValuesChanged(ulong)),
            this,   SLOT(gpioValuesChanged(ulong)));

    // Prepare GUI
    clear();
    printMenu();
    ui->testGroupBox->setEnabled(true);
    ui->spinBox->setFocus(Qt::TabFocusReason);
    ui->startPushButton->setDefault(true);
}


void TestWidget::startStopExit()
{
    if(m_ph == 0)
        return;


    int states = 1;

    switch(m_currentTestState)
    {
    case IDLE:
        clear();

        ui->spinBox->setEnabled(false);
        ui->startPushButton->setEnabled(false);
        qApp->processEvents();

        m_currentTestRoutine = ui->spinBox->value();

        test(m_currentTestRoutine, true, states);

        ui->startPushButton->setEnabled(true);
        qApp->processEvents();

        if(states == 1)
        {
            m_currentTestState = FINISHED;
            ui->startPushButton->setText("Exit");
        }
        else
        {
            m_currentTestState = RUNNING;
            ui->startPushButton->setText("Stop");
        }

        break;

    case RUNNING:
        test(m_currentTestRoutine, false, states);
        ui->startPushButton->setText("Exit");

        m_currentTestState = FINISHED;
        break;

    case FINISHED:
        ui->spinBox->setEnabled(true);
        ui->startPushButton->setText("Start");
        ui->spinBox->setFocus(Qt::TabFocusReason);

        clear();
        printMenu();

        m_currentTestState = IDLE;
        break;

    }

}

void TestWidget::test(int id, bool start, int &states)
{
    switch(id)
    {
    default:
    case 1:
        test_ReaderInfo(start, states);
        break;
    case 2:
        test_Attenuation(start, states);
        break;
    case 3:
        test_Frequency(start, states);
        break;
    case 4:
        test_Sensitivity(start, states);
        break;
    case 5:
        test_Heartbeat(start, states);
        break;
    case 6:
        test_GPIO(start, states);
        break;
    case 8:
        test_Inventory(start, states);
        break;
    case 9:
        test_SingleInventory(start, states);
        break;

    case 23:
        test_ReadTID(start, states);
        break;
    case 24:
        test_ReadWriteUser(start, states);
        break;

    case 31:
        test_AN001_ReadTIDFirstTag_Slow(start, states);
        break;
    }
}

void TestWidget::test_ReaderInfo(bool start, int &states)
{
    states = 1;

    if(!start)
        return;

    bool ok = false;

    // get reader id
    ulong readerId = 0;
    ok = m_ph->getReaderID(readerId);
    if (!ok)
        print("ERROR: Could not get ReaderID");

    // get reader type
    ulong readerType = 0;
    ok = m_ph->getReaderType(readerType);
    if (!ok)
        print("ERROR: Could not get ReaderType");

    // get hardware revision
    ulong hwRev = 0;
    ok = m_ph->getHardwareRevision(hwRev);
    if (!ok)
        print("ERROR: Could not get HardwareRevision");

    // get software revision
    ulong swRev = 0;
    ok = m_ph->getSoftwareRevision(swRev);
    if (!ok)
        print("ERROR: Could not get SoftwareRevision");

    // get bootloader revision
    ulong blRev = 0;
    ok = m_ph->getBootloaderRevision(blRev);
    if (!ok)
        print("ERROR: Could not get BootloaderRevision");

    // get current system
    QString system = 0;
    ok = m_ph->getCurrentSystem(system);
    if (!ok)
        print("ERROR: Could not get CurrentSystem");

    // get current state
    QrfeReaderInterface::eRFE_CURRENT_READER_STATE state = QrfeReaderInterface::RFE_STATE_IDLE;
    ok = m_ph->getCurrentState(state);
    if (!ok)
        print("ERROR: Could not get CurrentState");

    // get status register
    qulonglong statusReg = 0;
    ok = m_ph->getStatusRegister(statusReg);
    if (!ok)
        print("ERROR: Could not get StatusRegister");

    // print out results
    print("Reader Information:");
    print("\t -> ReaderID       = " + QString("%1").arg(readerId, 8, 16, QChar('0')).toLatin1());
    print("\t -> ReaderType     = " + QString("%1").arg(readerType, 8, 16, QChar('0')).toLatin1());
    print("\t -> HardwareRev    = " + QString("%1").arg(hwRev, 8, 16, QChar('0')).toLatin1());
    print("\t -> SoftwareRev    = " + QString("%1").arg(swRev, 8, 16, QChar('0')).toLatin1());
    print("\t -> BootloaderRev  = " + QString("%1").arg(blRev, 8, 16, QChar('0')).toLatin1());
    print("\t -> Current System = " + system.toLatin1());
    print("\t -> Current State  = " + QString("%1").arg(state).toLatin1());
    print("\t -> StatusRegister = " + QString("%1").arg(statusReg, 16, 16, QChar('0')).toLatin1());
}

void TestWidget::test_Attenuation(bool start, int &states)
{
    states = 1;

    if(!start)
        return;

    print("Testing attenuation settings:");

    ushort attMax, attCur, attTemp;

    // get attenuation values
    print("\t -> 1, Reading attenuation settings:");
    m_ph->getAttenuation(attMax, attCur);
    print("\t\t Attenuation Max=" + QString::number(attMax) + "Current=" + QString::number(attCur));
    print("");

    // set attenuation to fix value 10
    print("\t -> 2, Setting attenuation settings:");
    m_ph->setAttenuation(10);
    print("\t\t Set Attenuation to 10");
    print("");

    // get attenuation settings and check if the fix value is set
    print("\t -> 3, Reading attenuation settings:");
    m_ph->getAttenuation(attMax, attTemp);
    print("\t\t Attenuation Max=" + QString::number(attMax) + "Current=" + QString::number(attTemp));
    print("\t\t Current attenuation " + QString::number(attTemp) + " == 10?");
    if (attTemp != 10)
    {
        print("ERROR------------------ Set Attenuation is not the Current Attenuation");
        return;
    }
    print("\t\t OK\n");

    // retore attenuation to the previous value
    print("\t -> 4, Restore attenuation settings:");
    m_ph->setAttenuation(attCur);
    print("\t\t Set Attenuation to 0");
    print("");

    // check the set values again
    print("\t -> 5, Reading attenuation settings:");
    m_ph->getAttenuation(attMax, attCur);
    print("\t\t Attenuation Max=" + QString::number(attMax) + "Current=" + QString::number(attCur));
    print("");
}

void TestWidget::test_Frequency(bool start, int &states)
{
    states = 1;

    if(!start)
        return;

    print("Testing frequency settings:");

    uchar mode, maxCount;
    QList<uint> frequ;
    QList<uint> tempFrequ;

    // get current frequency table
    print("\t -> 1, Reading frequency settings:");
    m_ph->getFrequency(mode, maxCount, frequ);
    print("\t\t FrequencyTable Mode=" + QString::number(mode) + " Max=" + QString::number(maxCount) + " Current=" + QString::number(frequ.size()));
    for (int i = 0; i < frequ.size(); i++)
    {
        print("\t\t\t " + QString::number(i) + " = " + QString::number(frequ[i]));
    }
    print("");
}

void TestWidget::test_Sensitivity(bool start, int &states)
{
    states = 1;

    if(!start)
        return;

    print("Testing sensitivity settings:");

    short maxSens, minSens, curSens, temSens, actSens;

    // get current sensitivity
    print("\t -> 1, Reading sensitivity settings:");
    m_ph->getSensitivity( maxSens,  minSens,  curSens);
    print("\t\t Sensitivity Max=" + QString::number(maxSens) + " Min=" + QString::number(minSens) + " Current=" + QString::number(curSens));
    print("");
}

void TestWidget::test_Heartbeat(bool start, int &states)
{
    states = 2;

    if(start)
    {
        print("Testing Heartbeat:");

        // turn on heartbeat with an interval of 250ms
        print("\t -> 1, Setting Heartbeat ON with interval 250ms");
        m_heartBeatTime.start();
        m_ph->setHeartBeat(QrfeReaderInterface::HEARTBEAT_ON, 250);
    }
    else
    {
        // turn off heartbeat
        print("\t -> 2, Setting Heartbeat OFF");
        m_ph->setHeartBeat(QrfeReaderInterface::HEARTBEAT_OFF, 250);
    }
}

void TestWidget::test_GPIO(bool start, int &states)
{
    states = 1;

    if(!start)
        return;

    print("Test GPIO - Output");

    ulong mask, output, input;
    if (!m_ph->getGPIOCaps(mask, output, input))
    {
        print("ERROR: Could not get GPIO Caps");
        return;
    }
    printf("Mask: %X", mask);
    printf("Output: %X", output);
    printf("Input: %X", input);
}

void TestWidget::test_Inventory(bool start, int &states)
{
    states = 2;

    if(start)
    {
        print("Testing Cyclic Inventory:");

        // turn on cyclic inventory
        print("\t -> 1, Starting Cyclic Inventory (Press Enter to Stop)");
        m_cyclicInvCount = 0;
        m_cyclicInvTime.start();
        m_ph->setCyclicInventory(true);
    }
    else
    {
        // turn off cyclic inventory and calculate read rate
        m_ph->setCyclicInventory(false);
        double readRate = (double)m_cyclicInvCount / ((double)m_cyclicInvTime.elapsed()/1000.0);
        print("\t -> 2, Stopped Cyclic Inventry with a ReadRate of " + QString::number(readRate) + " reads/sec");
    }
}

void TestWidget::test_SingleInventory(bool start, int &states)
{
    states = 1;

    if(!start)
        return;

    // do a single inventory and print out the list
    print("Do SingleInventory");
    QList<QrfeReaderInterface::QrfeTagEvent> tagList;
    m_ph->doSingleInventory(tagList);
    print("\t\t -> Found " + QString::number(tagList.size()) + " Tags");

    QStringList result;
    foreach (QrfeReaderInterface::QrfeTagEvent tag, tagList)
    {
        result << tag.toString();
    }

    result.sort();
    uchar i = 1;
    foreach (QString str, result)
        print("\t\t #" + QString("%1").arg(i++, 2) + " " + str);
}

void TestWidget::test_ReadTID(bool start, int &states)
{
    states = 1;

    if(!start)
        return;

    print("Trying to read tid register:");

    // do a single inventory and print out the tag list with an index to select one tag
    print("\t -> 1, Searching for tags:");
    QList<QrfeReaderInterface::QrfeTagEvent> tagList;
    m_ph->doSingleInventory(tagList);
    print("\t\t -> Found " + QString::number(tagList.size()) + " Tags");

    print("");
    print("\t -> 2, Select a tag by index: > ");
    QByteArray selectedEpc;
    if (tagList.size() > 0)
    {
        QStringList tags;
        foreach (QrfeReaderInterface::QrfeTagEvent tag, tagList)
            tags << tag.tagId.toHex();

        bool ok = false;
        QString tag = QInputDialog::getItem(this, "Select tag", "Select tag:", tags, 0, false, &ok);
        if(!ok || tag.isNull())
            return;

        selectedEpc = QByteArray::fromHex(tag.toLatin1());
    }
    else
    {
        print("\t\t Found 0 Tags, stopping the test...");
        return;
    }

    print("");
    // try to read the first 4 bytes of the TID membank of the selected tag
    print("\t -> 3, Trying to read first 4 bytes of TID register:");
    QByteArray passwd(4, (char)0);
    QByteArray data;
    if (m_ph->readFromTag(selectedEpc, 0x02, 0, passwd, 4, data))
    {
        print("\t\t Read succeeded, read data: " + data.toHex());
    }
    else
    {
        print("\t\t Read failed:" + QrfeReaderInterface::toString(m_ph->getLastReturnCode()));
    }
    print("");
}

void TestWidget::test_ReadWriteUser(bool start, int &states)
{
    states = 1;

    if(!start)
        return;


    print("Trying to read write user register:");

    // do a single inventory and print out the tag list with an index to select one tag
    print("\t -> 1, Searching for tags:");
    QList<QrfeReaderInterface::QrfeTagEvent> tagList;
    m_ph->doSingleInventory(tagList);
    print("\t\t -> Found " + QString::number(tagList.size()) + " Tags");

    print("");
    print("\t -> 2, Select a tag by index: > ");
    QByteArray selectedEpc;
    if (tagList.size() > 0)
    {
        QStringList tags;
        foreach (QrfeReaderInterface::QrfeTagEvent tag, tagList)
            tags << tag.tagId.toHex();

        bool ok = false;
        QString tag = QInputDialog::getItem(this, "Select tag", "Select tag:", tags, 0, false, &ok);
        if(!ok || tag.isNull())
            return;

        selectedEpc = QByteArray::fromHex(tag.toLatin1());
    }
    else
    {
        print("\t\t Found 0 Tags, stopping the test...");
        return;
    }

    uchar memBank = 0x03;
    QByteArray passwd(4, (char)0);
    QByteArray userMemBefore;
    QByteArray userMemAfter;
    QByteArray old4Bytes;
    QByteArray new4Bytes;

    new4Bytes.append(0x11);
    new4Bytes.append(0x22);
    new4Bytes.append(0x33);
    new4Bytes.append(0x44);

    // try to read the whole user memory bank
    print("\t -> 3, Trying to Read 4 bytes from user mem");
    if (m_ph->readFromTag(selectedEpc, memBank, 0, passwd, 4, userMemBefore))
    {
        print("\t\t Read succeeded, read data:");
        QString mem;
        for (int i = 0; i < userMemBefore.size(); i++)
        {
            mem += (QString("%1-").arg(userMemBefore[i], 2, 16, QChar('0')));
            if(((i + 1)%8) == 0)
                mem += ("\n\t\t\t ");
        }
        print("\t\t\t " + mem);
        print("\n");
    }
    else
    {
        print("\t\t Read failed:" + QrfeReaderInterface::toString(m_ph->getLastReturnCode()));
        return;
    }
    print("");

    // save the old first 4 bytes of the user memory
    old4Bytes = userMemBefore.left(4);

    // try to write 4 dummy bytes to the user mem
    print("\t -> 4, Trying to Wrtie data:");
    print("\t\t\t " + new4Bytes.toHex());
    if (m_ph->writeToTag(selectedEpc, memBank, 0, passwd, new4Bytes))
    {
        print("\t\t Wrtie succeeded");
    }
    else
    {
        print("\t\t Write failed:" + QrfeReaderInterface::toString(m_ph->getLastReturnCode()));
        return;
    }
    print("");

    // try to read the whole user me again
    print("\t -> 5, Trying to Read written data");
    if (m_ph->readFromTag(selectedEpc, memBank, 0, passwd, 4, userMemAfter))
    {
        print("\t\t Read succeeded, read data:");
        QString mem;
        for (int i = 0; i < userMemAfter.size(); i++)
        {
            mem += (QString("%1-").arg(userMemAfter[i], 2, 16, QChar('0')));
            if(((i + 1)%8) == 0)
                mem += ("\n\t\t\t ");
        }
        print("\t\t\t " + mem);
        print("\n");
    }
    else
    {
        print("\t\t Read failed:" + QrfeReaderInterface::toString(m_ph->getLastReturnCode()));
        return;
    }
    print("");

    // try to write the old bytes into the memory
    print("\t -> 6, Trying to restore the bytes");
    if (m_ph->writeToTag(selectedEpc, memBank, 0, passwd, old4Bytes))
    {
        print("\t\t Wrtie succeeded");
    }
    else
    {
        print("\t\t Write failed:" + QrfeReaderInterface::toString(m_ph->getLastReturnCode()));
        return;
    }
    print("");

    // again read the restored bytes from the tag
    print("\t -> 7, Trying to Read written data");
    if (m_ph->readFromTag(selectedEpc, memBank, 0, passwd, 4, userMemAfter))
    {
        print("\t\t Read succeeded, read data:");
        QString mem;
        for (int i = 0; i < userMemAfter.size(); i++)
        {
            mem += (QString("%1-").arg(userMemAfter[i], 2, 16, QChar('0')));
            if(((i + 1)%8) == 0)
                mem += ("\n\t\t\t ");
        }
        print("\t\t\t " + mem);
        print("\n");
    }
    else
    {
        print("\t\t Read failed:" + QrfeReaderInterface::toString(m_ph->getLastReturnCode()));
        return;
    }
    print("");
}

void TestWidget::test_AN001_ReadTIDFirstTag_Slow(bool start, int &states)
{
    states = 1;

    if(!start)
        return;


    print("Trying to read tid register:");


    print("\t -> 1, Searching for tags:");

    m_cyclicInvCount = 0;
    m_lastTagEventValid = false;
    QTime startTime;

    m_ph->setCyclicInventory(true);
    bool res = waitForSignal(m_ph, SIGNAL(cyclicInventory(QrfeReaderInterface::QrfeTagEvent)), 5000);
    m_ph->setCyclicInventory(false);
    startTime.start();

    if(!res)
    {
        print("\t\t Did not find any tag...");
        return;
    }

    print("\t\t Found Tag " + m_lastTagEvent.tagId.toHex());

    // try to read the first 4 bytes of the TID membank of the detected tag
    print("\t -> 2, Trying to read first 4 bytes of TID register:");
    QByteArray passwd(4, (char)0);
    QByteArray data;
    if (m_ph->readFromTag(m_lastTagEvent.tagId, 0x02, 0, passwd, 4, data))
    {
        print("\t\t Read succeeded, read data: " + data.toHex());
    }
    else
    {
        print("\t\t Read failed:" + QrfeReaderInterface::toString(m_ph->getLastReturnCode()));
    }
    print("");
    print("Read Result " + QString::number(startTime.elapsed()) + "ms after the tag was detected");
    print("");
}

void TestWidget::heartBeat ()
{
    uint elapsed = m_heartBeatTime.elapsed();
    m_heartBeatTime.start();
    print("[E] Heartbeat after " + QString::number(elapsed));
}

void TestWidget::cyclicInventory (const QrfeReaderInterface::QrfeTagEvent &tagInfo)
{
    if(!m_lastTagEventValid)
    {
        m_lastTagEventValid = true;
        m_lastTagEvent = tagInfo;
    }

    QString info;
    info = "\t[E] " + QString("%1").arg(++m_cyclicInvCount, 8, 10, QChar(' ')) + " " + tagInfo.tagId.toHex() + " ";

    if (tagInfo.hasMemory)
    {
        info += " MEM@" + QString::number(tagInfo.memBank) + "." + QString::number(tagInfo.memAddr) + ":" + tagInfo.memData.toHex() + " ";
    }

    if (tagInfo.hasApplicationInfo)
    {
        info += " APP:" + tagInfo.applicationInfo.toHex() + " ";
    }

    print(info);
}

void TestWidget::stateChanged (const int newState)
{
    print("[E] State changed to: " + QrfeReaderInterface::toString((QrfeReaderInterface::eRFE_CURRENT_READER_STATE)newState));
}

void TestWidget::statusRegisterChanged (const qulonglong statusRegister)
{
    print("[E] Status register changed " + QString("0x%1").arg(statusRegister, 16, 16, QChar('0')));
}

void TestWidget::gpioValuesChanged ( const ulong gpioValues )
{
    print("[E] GPIO values changed " + QString("0x%1").arg(gpioValues, 8, 16, QChar('0')));
}



void TestWidget::printMenu()
{
    clear();
    print("+------------------------ MENU ----------------------------+");
    print("|    1 = Query Reader Information                          |");
    print("|    2 = Test Attenuation Settings                         |");
    print("|    3 = Read Frequency Settings                           |");
    print("|    4 = Read Sensitivity Settings                         |");
    print("|    5 = Test Heartbeat                                    |");
    print("|    6 = Test GPIO                                         |");
    print("|    8 = Start Inventory                                   |");
    print("|    9 = Do SinglInventory                                 |");
    print("|   23 = Try to Read TID                                   |");
    print("|   24 = Try to Read/Write User Mem                        |");
    print("|   31 = Test Read TID of First Tag - Slow                 |");
    print("+----------------------------------------------------------+");
    print("");
    print("Please choose: > ");
}

void TestWidget::print(const QString& str)
{
    ui->textBrowser->append(str);
    qApp->processEvents();
}

void TestWidget::clear()
{
    ui->textBrowser->clear();
    qApp->processEvents();
}


bool TestWidget::waitForSignal(QObject *sender, const char *signal, int timeout)
{
    QEventLoop loop;
    QTimer timer;
    timer.setInterval(timeout);
    timer.setSingleShot(true);

    loop.connect(sender, signal, SLOT(quit()));
    loop.connect(&timer, SIGNAL(timeout()), SLOT(quit()));
    timer.start();
    loop.exec();

    return timer.isActive();
}



